/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/common.hpp>
#include <baja/diagnostics_config.hpp>
#include <baja/trace.hpp>
#include <baja/callstack_fwd.hpp>

#ifdef BAJA_WIN32
#include <windows.h>
#endif

// configuration
// BAJA_CONFIG_ASSERT_USE_STD - define this to use assert.h
// BAJA_CONFIG_ASSERT_USE_CRT - use crtdbg.h

// try to use crt dbg by default
#if !defined(BAJA_CONFIG_ASSERT_USE_STD)
#define BAJA_CONFIG_ASSERT_USE_CRT
#endif

#ifdef BAJA_DEBUG
#ifdef BAJA_CONFIG_ASSERT_USE_CRT
#include <crtdbg.h>
#else
#include <assert.h>
#endif
#endif // BAJA_DEBUG

// note assert doesn't support wide char messages (i.e. anything that can be streamed into an ostringstream)
// anything else will give compilation errors
namespace baja {

template <class Tmsg> 
BAJA_INLINE static void traceAssert(
    const char* expr,
    const Tmsg& msg,
    const char* file,
    uint32 line
    )
{
    // using string stream so we can leverage all the << operators defined elesehwere to allow for more 
    // types to accept as parameters
    std::ostringstream sstm;

    sstm << "--------------------------------------------------------------------------------";
    sstm << "\n!! ASSERTION FAILED: " << expr << "\nmessage: " << msg << "\nfile: " << file << "\nline: " << line;
    
    if (!diagnostics::config::noStack)
    {
        sstm << "\nstack trace:" << getStackTrace(
            2, // skip two frames
            diagnostics::constants::totalStackFrames,
            false // don't throw if anything happens
            );
    }

    sstm << "\n--------------------------------------------------------------------------------";
    BAJA_TRACE(sstm.str());
}
#ifdef BAJA_DEBUG
#define TRACE_ASSERT(expr, msg, file, line) (void)(!!(expr) || (::baja::traceAssert(#expr, msg, file, line), 0))
#else
#define TRACE_ASSERT(expr, msg, file, line)
#endif

#ifdef BAJA_CONFIG_ASSERT_USE_CRT
#define BAJA_ASSERT_IMPL(expr, msg) \
    if (!(expr)) \
    { \
        ::_CrtDbgBreak(); \
    }
#elif defined(BAJA_CONFIG_ASSERT_USE_STD)
#define BAJA_ASSERT_IMPL(expr_, msg_) \
    if (!(expr_)) _wassert(MAKE_WIDE(expr_), MAKE_WIDE(__FILE__), __LINE__);
#endif

} // namespace baja

#ifdef BAJA_DEBUG

#define BAJA_ASSERT(expr, msg) \
    TRACE_ASSERT(expr, msg, __FILE__, __LINE__); \
    BAJA_ASSERT_IMPL(expr, msg)
#define BAJA_VERIFY(expr) BAJA_ASSERT(expr, "(verification failed)")
#define BAJA_FAIL(msg) BAJA_ASSERT(0, msg)

#else // !BAJA_DEBUG

#define BAJA_ASSERT
#define BAJA_VERIFY
#define BAJA_FAIL

#endif // BAJA_DEBUG

#include <baja/callstack.hpp>
